package com.sirui.cloud.identity.global;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Part;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;

import com.sirui.cloud.identity.domain.MsgDto;
import com.sirui.x.global.LogArround;
import com.sirui.x.global.MsgException;
import com.sirui.x.util.JacksonUtil;

@Component
@Aspect
public class LoggerAspect {
	private static final Logger logger = LoggerFactory.getLogger(LoggerAspect.class);

	@Pointcut("@annotation(com.sirui.x.global.LogArround)")
	private void pointcut() {
	}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint joinPoint) {

		Signature signature = joinPoint.getSignature();
		String classSimpleName = signature.getDeclaringType().getSimpleName();
		String methodName = signature.getName();

		Object[] args = joinPoint.getArgs();
		LogArround logArraound = getByAnnotation(signature, LogArround.class);
		if (logArraound.before()) {
			if (signature instanceof CodeSignature) {
				CodeSignature methodSignature = (CodeSignature) signature;
				logkv(methodSignature, args, classSimpleName, methodName);
			} else {
				logv(args, classSimpleName, methodName);
			}
		}

		try {
			Object proceed = joinPoint.proceed();
			if (logArraound.after()) {
				logger.info("{{}.{} resp: {}}", classSimpleName, methodName, JacksonUtil.bean2String(proceed));
			}
			return proceed;
		} catch (Throwable e) {
			String errorMsg = String.format("%s.%s resp: error", classSimpleName, methodName);
			logger.error(errorMsg, e);
			return handleException(e);
		}

	}

	private <T extends Annotation> T getByAnnotation(Signature signature, Class<T> valueType) {
		if (signature instanceof MethodSignature) {
			MethodSignature methodSignature = (MethodSignature) signature;
			return methodSignature.getMethod().getAnnotation(valueType);
		} else {
			return null;
		}
	}

	private void logkv(CodeSignature signature, Object[] args, String classSimpleName, String methodName) {
		HashMap<String, Object> param = new HashMap<>();
		String[] names = signature.getParameterNames();
		for (int i = 0; i < args.length; i++) {
			if (ifLog(args[i])) {
				param.put(names[i], args[i]);
			}
		}
		logger.info("{{}.{} req: {}}", classSimpleName, methodName, JacksonUtil.bean2String(param));
	}

	private void logv(Object[] args, String classSimpleName, String methodName) {
		List<Object> param = new ArrayList<>(args.length);
		for (int i = 0; i < args.length; i++) {
			if (ifLog(args[i])) {
				param.add(args[i]);
			}
		}
		logger.info("{{}.{} req: {}}", classSimpleName, methodName, JacksonUtil.bean2String(param));
	}

	private boolean ifLog(Object arg) {
		return !(arg instanceof ServletRequest) && !(arg instanceof BindingResult) && !(arg instanceof MultipartFile)
				&& !(arg instanceof Part) && !(arg instanceof ServletResponse);
	}

	private Object handleException(Throwable e) {

		if (e instanceof MsgException) {
			return MsgDto.getFail(e.getMessage());
		} else {
			return MsgDto.getFail("服务忙，请稍后再试");
		}
	}

}
